#pragma once

struct float3
{
	float x,y,z;

	float3(){}
	float3(float ix,float iy, float iz):x(ix),y(iy),z(iz){}
	void set(float ix,float iy, float iz){x=ix;y=iy;z=iz;}

	float& operator[](int index){return *(&x+index);}
	const float& operator[] (int index) const{return *(&x+index);}

	float3 operator-(){return float3(-x,-y,-z);}

	inl float3 operator+(const float3& v) const
	{
		return float3(x+v.x,y+v.y,z+v.z);
	}
	inl float3 operator-(const float3& v) const
	{
		return float3(x-v.x,y-v.y,z-v.z);
	}

	inl friend float3 cross(const float3& v1, const float3& v2)
	{
		return float3(
			v1.y*v2.z-v1.z*v2.y,
			v1.z*v2.x-v1.x*v2.z,
			v1.x*v2.y-v1.y*v2.x
			);
	}

	inl void cross(const float3& v1, const float3& v2)
	{
		x=v1.y*v2.z-v1.z*v2.y;
		y=v1.z*v2.x-v1.x*v2.z;
		z=v1.x*v2.y-v1.y*v2.x;
	}

	void operator*=(float s)
	{
		x*=s;
		y*=s;
		z*=s;
	}
	void operator/=(float s)
	{
		x/=s;
		y/=s;
		z/=s;
	}

	void operator +=(const float3& v)
	{
		x+=v.x;
		y+=v.y;
		z+=v.z;
	}

	void operator -=(const float3& v)
	{
		x-=v.x;
		y-=v.y;
		z-=v.z;
	}

	float3 operator*(float s) const
	{
		return float3(x*s,y*s,z*s);
	}
	friend float3 operator*(float s,const float3& v)
	{
		return float3(v.x*s,v.y*s,v.z*s);
	}
	float3 operator/(float s) const
	{
		return float3(x/s,y/s,z/s);
	}
	void normalize()
	{
		float length(const float3& v);
		float l=length(*this);
		x/=l;
		y/=l;
		z/=l;
	}

};

inl float length2(const float3& v)
{
	return v.x*v.x+v.y*v.y+v.z*v.z;
}

inl float length(const float3& v)
{
	return sqrtf(length2(v));
}

inl float dot(const float3& v1, const float3& v2)
{
	return v1.x*v2.x+v1.y*v2.y+v1.z*v2.z;
}


inl void normalize(float3& v)
{
	float l=length(v);
	v.x/=l;
	v.y/=l;
	v.z/=l;
}

inl float3 rotate(const float3& i_src, const float3& i_axis, float i_angle)
{
	float3 a=dot(i_src,i_axis)*i_axis;
	float3 b=i_src-a;
	float3 c; c.cross(i_axis,b);
	return b*cosf(i_angle)+c*sinf(i_angle)+a;
}

inl float3 rotate(const float3& i_src, const float3& i_axis, float sinangle, float cosangle)
{
	float3 a=dot(i_src,i_axis)*i_axis;
	float3 b=i_src-a;
	float3 c; c.cross(i_axis,b);
	return b*cosangle+c*sinangle+a;
}

inl float3 lerp(const float3& p0, const float3& p1, float weight)
{
	return (p1*weight)+(p0*(1.0f-weight));
}
#pragma once

	struct double3
	{
		double3();
		double3(double i_xcoord, double i_ycoord, double i_zcoord);
		double3(const double* i_src);

		explicit double3(const float3& src)
		{
			x=src.x;
			y=src.y;
			z=src.z;
		}

		double&	operator[](int i_index);
		const double& operator[](int i_index) const;

		void	clear();
		void	set(double i_xcoord, double i_ycoord, double i_zcoord);

		void	cross(const double3& i_src1, const double3& i_src2);
		friend 
		double3 cross(const double3& i_src1, const double3& i_src2);
		double3 cross(const double3& i_other) const;
		
		double	dot(const double3& i_other) const;
		friend
		double	dot(const double3& i_v1, const double3& i_v2);

		double	squarelength() const;
		double	length() const;
		void	normalize();
		void	normalizesafe();

		void rotate(const double3& i_src, const double3& i_axis, double i_angle);

		void	abs(const double3& i_src);

		double3	operator+(const double3& i_other) const;
		double3	operator-(const double3& i_other) const;
		void	operator+=(const double3& i_other);
		void	operator-=(const double3& i_other);

		double3	operator*(double i_scalar) const;
		double3	operator/(double i_scalar) const;

		friend
		double3 operator*(double i_scalar, const double3& i_vector);

		void	operator*=(double i_scalar);
		void	operator/=(double i_scalar);

		double3	operator-() const;

//		void max(const double3& i_v1, const double3& i_v2);
//		void max(const double3& i_other);
//		void min(const double3& i_v1, const double3& i_v2);
//		void min(const double3& i_other);

		double maxelem() const;

		void interpolate(const double3& i_src1, const double3& i_src2, double i_time);


		double x,y,z;
	};//double3

	inl double3::double3()
	{
	}

	inl double3::double3(double i_xcoord, double i_ycoord, double i_zcoord)
	{
		x=i_xcoord;
		y=i_ycoord;
		z=i_zcoord;
	}

	inl double3::double3(const double* i_src)
	{
		x=i_src[0];
		y=i_src[1];
		z=i_src[2];
	}


	inl double& double3::operator[](int i_index)
	{
		return *(((double*)this)+i_index);
	}

	inl const double& double3::operator[](int i_index) const
	{
		return *(((double*)this)+i_index);
	}

	inl void double3::clear()
	{
		x=y=z=0.0f;
	}

	inl void double3::set(double i_xcoord, double i_ycoord, double i_zcoord)
	{
		x=i_xcoord;
		y=i_ycoord;
		z=i_zcoord;
	}


	inl void	double3::cross(const double3& i_src1, const double3& i_src2)
	{
		x=i_src1.y*i_src2.z-i_src1.z*i_src2.y;
		y=i_src1.z*i_src2.x-i_src1.x*i_src2.z;
		z=i_src1.x*i_src2.y-i_src1.y*i_src2.x;
	}

	inl double3 cross(const double3& i_src1, const double3& i_src2)
	{
		return double3(
			i_src1.y*i_src2.z-i_src1.z*i_src2.y,
			i_src1.z*i_src2.x-i_src1.x*i_src2.z,
			i_src1.x*i_src2.y-i_src1.y*i_src2.x
			);
	}

	inl double3 double3::cross(const double3& i_other) const
	{
		return ::cross(*this,i_other);
	}

	inl double	double3::dot(const double3& i_other) const
	{
		return x*i_other.x+y*i_other.y+z*i_other.z;
	}

	inl double dot(const double3& i_v1, const double3& i_v2)
	{
		return i_v1.x*i_v2.x+i_v1.y*i_v2.y+i_v1.z*i_v2.z;
	}

	inl double	double3::squarelength() const
	{
		return x*x+y*y+z*z;
	}

	inl double	double3::length() const
	{
		return sqrt(squarelength());
	}

	inl void	double3::normalize()
	{
		double l=length();

		x/=l;
		y/=l;
		z/=l;
	}

	inl void	double3::normalizesafe()
	{
		double sqlength=squarelength();

		if (sqlength>0.000001f)
		{
			double l=sqrt(sqlength);

			x/=l;
			y/=l;
			z/=l;
		}
		else
		{
			x=y=z=0;
		}
	}

	inl double3	double3::operator+(const double3& i_other) const
	{
		return double3(x+i_other.x,y+i_other.y,z+i_other.z);
	}

	inl double3	double3::operator-(const double3& i_other) const
	{
		return double3(x-i_other.x,y-i_other.y,z-i_other.z);
	}

	inl void	double3::operator+=(const double3& i_other)
	{
		x+=i_other.x;
		y+=i_other.y;
		z+=i_other.z;
	}

	inl void	double3::operator-=(const double3& i_other)
	{
		x-=i_other.x;
		y-=i_other.y;
		z-=i_other.z;
	}

	inl double3	double3::operator*(double i_scalar) const
	{
		return double3(x*i_scalar,y*i_scalar,z*i_scalar);
	}

	inl double3	double3::operator/(double i_scalar) const
	{
		return double3(x/i_scalar,y/i_scalar,z/i_scalar);
	}

	inl double3 operator*(double i_scalar, const double3& i_vector)
	{
		return i_vector*i_scalar;
	}

	inl void	double3::operator*=(double i_scalar)
	{
		x*=i_scalar;
		y*=i_scalar;
		z*=i_scalar;
	}

	inl void	double3::operator/=(double i_scalar)
	{
		x/=i_scalar;
		y/=i_scalar;
		z/=i_scalar;
	}

	inl double3	double3::operator-() const
	{
		return double3(-x,-y,-z);
	}

	inl void double3::abs(const double3& i_src)
	{
		x=fabs(i_src.x);
		y=fabs(i_src.y);
		z=fabs(i_src.z);
	}

	inl double double3::maxelem() const
	{
		double r=x;
		if (y>r) r=y;
		if (z>r) r=z;

		return r;
	}

	inl void double3::rotate(const double3& i_src, const double3& i_axis, double i_angle)
	{
		double3 a=i_src.dot(i_axis)*i_axis;
		double3 b=i_src-a;
		double3 c; c.cross(i_axis,b);
		*this=b*cos(i_angle)+c*sin(i_angle)+a;
	}

	inl void getorthogonalaxes(double3& o_axis1, double3& o_axis2, const double3& i_axissrc)
	{
		if ((i_axissrc.z) > 0.707f || i_axissrc.z<-0.707f)
		{
			// y-z plane
			double  a = i_axissrc.y*i_axissrc.y + i_axissrc.z*i_axissrc.z;
			double k = 1/sqrt(a);
			o_axis1.x = 0;
			o_axis1.y = -i_axissrc.z*k;
			o_axis1.z = i_axissrc.y*k;

			o_axis2.x = a*k;
			o_axis2.y = -i_axissrc.x*o_axis1.z;
			o_axis2.z = i_axissrc.x*o_axis1.y;
		}
		else
		{
			// x-y plane
			double a = i_axissrc.x*i_axissrc.x + i_axissrc.y*i_axissrc.y;
			double k = 1/sqrt(a);
			o_axis1.x = -i_axissrc.y*k;
			o_axis1.y = i_axissrc.x*k;
			o_axis1.z = 0;

			o_axis2.x = -i_axissrc.z*o_axis1.y;
			o_axis2.y = i_axissrc.z*o_axis1.x;
			o_axis2.z = a*k;
		}
	}

	inl void double3::interpolate(const double3& i_src1, const double3& i_src2, double i_time)
	{
		x=i_src1.x+i_time*(i_src2.x-i_src1.x);
		y=i_src1.y+i_time*(i_src2.y-i_src1.y);
		z=i_src1.z+i_time*(i_src2.z-i_src1.z);
	}

	inl void dvec3_to_float3(float3& dst, const double3& src)
	{
		dst.set(float(src.x),float(src.y),float(src.z));
	}
	inl float3 dvec3_to_float3(const double3& src)
	{
		return float3(float(src.x),float(src.y),float(src.z));
	}

